namespace WpfWatermarkTextBox.Behaviors
{
	#region #using Directives

	using System;
	using System.ComponentModel;
	using System.Windows;
	using System.Windows.Controls;

	#endregion

	public sealed class WatermarkPasswordBoxBehavior
	{
		private readonly PasswordBox m_PasswordBox;
		private TextBlockAdorner m_TextBlockAdorner;

		private WatermarkPasswordBoxBehavior(PasswordBox passwordBox)
		{
			if (passwordBox == null)
			{
				throw new ArgumentNullException("passwordBox");
			}

			this.m_PasswordBox = passwordBox;
		}

		#region Behavior Internals

		public static readonly DependencyProperty EnableWatermarkProperty = DependencyProperty.RegisterAttached("EnableWatermark", typeof(bool),
		                                                                                                        typeof(WatermarkPasswordBoxBehavior),
		                                                                                                        new UIPropertyMetadata(false,
		                                                                                                                               OnEnableWatermarkChanged));

		private static readonly DependencyProperty WatermarkPasswordBoxBehaviorProperty = DependencyProperty.RegisterAttached(
			"WatermarkPasswordBoxBehavior", typeof(WatermarkPasswordBoxBehavior), typeof(WatermarkPasswordBoxBehavior), new UIPropertyMetadata(null));

		private static WatermarkPasswordBoxBehavior GetWatermarkPasswordBoxBehavior(DependencyObject obj)
		{
			return (WatermarkPasswordBoxBehavior) obj.GetValue(WatermarkPasswordBoxBehaviorProperty);
		}

		private static void SetWatermarkPasswordBoxBehavior(DependencyObject obj, WatermarkPasswordBoxBehavior value)
		{
			obj.SetValue(WatermarkPasswordBoxBehaviorProperty, value);
		}

		public static bool GetEnableWatermark(PasswordBox obj)
		{
			return (bool) obj.GetValue(EnableWatermarkProperty);
		}

		public static void SetEnableWatermark(PasswordBox obj, bool value)
		{
			obj.SetValue(EnableWatermarkProperty, value);
		}

		private static void OnEnableWatermarkChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			if (e.OldValue != null)
			{
				bool enabled = (bool) e.OldValue;

				if (enabled)
				{
					var passwordBox = (PasswordBox) d;
					var behavior = GetWatermarkPasswordBoxBehavior(passwordBox);
					behavior.Detach();

					SetWatermarkPasswordBoxBehavior(passwordBox, null);
				}
			}

			if (e.NewValue != null)
			{
				bool enabled = (bool) e.NewValue;

				if (enabled)
				{
					var passwordBox = (PasswordBox) d;
					var behavior = new WatermarkPasswordBoxBehavior(passwordBox);
					behavior.Attach();

					SetWatermarkPasswordBoxBehavior(passwordBox, behavior);
				}
			}
		}

		private void Attach()
		{
			this.m_PasswordBox.Loaded += PasswordBoxLoaded;
			this.m_PasswordBox.PasswordChanged += PasswordBoxPasswordChanged;
			this.m_PasswordBox.DragEnter += PasswordBoxDragEnter;
			this.m_PasswordBox.DragLeave += PasswordBoxDragLeave;
		}

		private void Detach()
		{
			this.m_PasswordBox.Loaded -= PasswordBoxLoaded;
			this.m_PasswordBox.PasswordChanged -= PasswordBoxPasswordChanged;
			this.m_PasswordBox.DragEnter -= PasswordBoxDragEnter;
			this.m_PasswordBox.DragLeave -= PasswordBoxDragLeave;
		}

		private void PasswordBoxDragLeave(object sender, DragEventArgs e)
		{
			UpdateAdorner();
		}

		private void PasswordBoxDragEnter(object sender, DragEventArgs e)
		{
			this.m_PasswordBox.TryRemoveAdorners<TextBlockAdorner>();
		}

		private void PasswordBoxPasswordChanged(object sender, RoutedEventArgs routedEventArgs)
		{
			bool hasText = !string.IsNullOrEmpty(this.m_PasswordBox.Password);
			SetHasText(this.m_PasswordBox, hasText);
		}

		private void PasswordBoxLoaded(object sender, RoutedEventArgs e)
		{
			Init();
		}

		#endregion

		#region Attached Properties

		private static readonly DependencyPropertyKey HasTextPropertyKey = DependencyProperty.RegisterAttachedReadOnly("HasText", typeof(bool),
		                                                                                                               typeof(WatermarkPasswordBoxBehavior),
		                                                                                                               new UIPropertyMetadata(false));

		public static readonly DependencyProperty HasTextProperty = HasTextPropertyKey.DependencyProperty;

		public static readonly DependencyProperty LabelProperty = DependencyProperty.RegisterAttached("Label", typeof(string),
		                                                                                              typeof(WatermarkPasswordBoxBehavior));

		public static readonly DependencyProperty LabelStyleProperty = DependencyProperty.RegisterAttached("LabelStyle", typeof(Style),
		                                                                                                   typeof(WatermarkPasswordBoxBehavior));

		public static string GetLabel(PasswordBox obj)
		{
			return (string) obj.GetValue(LabelProperty);
		}

		public static void SetLabel(PasswordBox obj, string value)
		{
			obj.SetValue(LabelProperty, value);
		}

		public static Style GetLabelStyle(PasswordBox obj)
		{
			return (Style) obj.GetValue(LabelStyleProperty);
		}

		public static void SetLabelStyle(PasswordBox obj, Style value)
		{
			obj.SetValue(LabelStyleProperty, value);
		}

		public static bool GetHasText(PasswordBox obj)
		{
			return (bool) obj.GetValue(HasTextProperty);
		}

		private static void SetHasText(PasswordBox obj, bool value)
		{
			obj.SetValue(HasTextPropertyKey, value);
		}

		#endregion

		private void Init()
		{
			this.m_TextBlockAdorner = new TextBlockAdorner(this.m_PasswordBox, GetLabel(this.m_PasswordBox), GetLabelStyle(this.m_PasswordBox));
			UpdateAdorner();

			var focusProp = DependencyPropertyDescriptor.FromProperty(UIElement.IsFocusedProperty, typeof(FrameworkElement));
			if (focusProp != null)
			{
				focusProp.AddValueChanged(this.m_PasswordBox, (sender, args) => UpdateAdorner());
			}

			var containsTextProp = DependencyPropertyDescriptor.FromProperty(HasTextProperty, typeof(PasswordBox));
			if (containsTextProp != null)
			{
				containsTextProp.AddValueChanged(this.m_PasswordBox, (sender, args) => UpdateAdorner());
			}
		}

		private void UpdateAdorner()
		{
			if (GetHasText(this.m_PasswordBox) || this.m_PasswordBox.IsFocused)
			{
				// Hide the Watermark Label if the adorner layer is visible
				this.m_PasswordBox.ToolTip = GetLabel(this.m_PasswordBox);
				this.m_PasswordBox.TryRemoveAdorners<TextBlockAdorner>();
			}
			else
			{
				// Show the Watermark Label if the adorner layer is visible
				this.m_PasswordBox.ToolTip = null;
				this.m_PasswordBox.TryAddAdorner<TextBlockAdorner>(this.m_TextBlockAdorner);
			}
		}
	}
}